﻿namespace UnityEditor.Controls.ProjectControl
{
    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.IO;
    using System.Linq;
    using System.Linq.Expressions;
    using System.Windows;
    using System.Windows.Controls;
    using UnityEditor.Controls.ProjectControl.ViewModel;
    using UnityEditor.IO;
    using UnityEngine.IO;
    using System.Windows.Input;
    using System.Diagnostics;
    using UnityEditor.Controls.HierarchyControl.ViewModel;
    using UnityEngine.Core;

    /// <summary>
    /// Interaction logic for FolderPicker.xaml
    /// </summary>
    public partial class ProjectControl : UserControl, INotifyPropertyChanged
    {
        #region Constants
        private const string EmptyItemName = "Empty";

        private const string NewFolderName = "New Folder";

        private const int MaxNewFolderSuffix = 10000;

        #endregion

        private ProjectItem root;

        private ProjectItem selectedItem;

        private string selectedPath = string.Empty;

        public ProjectControl()
        {
            this.InitializeComponent();

            this.Init();
        }

        public event PropertyChangedEventHandler PropertyChanged;

        /// <summary>
        /// Get/set root item of tree view
        /// </summary>
        public ProjectItem Root
        {
            get
            {
                return this.root;
            }

            private set
            {
                // for some (unknown) reason this cannot be guarded with an if != value block ???
                this.root = value;
                this.NotifyPropertyChanged(() => this.Root);
            }
        }

        /// <summary>
        /// Get/set currently selected item in the tree view
        /// </summary>
        public ProjectItem SelectedItem
        {
            get
            {
                return this.selectedItem;
            }
            private set
            {
                if (this.selectedItem != value)
                {
                    this.selectedItem = value;

                    if (value != null)
                    {
                        this.SelectedPath = this.selectedItem.RelativePath;
                        //if (IOManager.Instance.IsValidSceneFile(this.SelectedPath, false))
                            //Workspace.Instance.GameScene = IOManager.Instance.LoadGameScene(this.SelectedPath, false);
                    }
                    else
                        this.SelectedPath = null;

                    this.NotifyPropertyChanged(() => this.SelectedItem);
                }
            }
        }

        /// <summary>
        /// Get/set path of the currently selected element in the treeview
        /// </summary>
        public string SelectedPath
        {
            get
            {
                return this.selectedPath;
            }

            // Setter is called by TreeView_SelectedItem event ONLY
            // It is currently not clear how to sync back new paths to the tree view - although its possible through the
            // UpdateInitialPathUI method but the textbox looses its focus if that method is used - so a method without loosing
            // focus would be preferable ...
            private set
            {
                if (value != this.selectedPath)
                {
                    this.selectedPath = value;

                    // Sync back to treeView Problem: TreeView acquires focus and textbox cannot be used for input
                    ////if (System.IO.Directory.Exists(this.selectedPath))
                    //// if (this.SelectedItem != null)
                    ////if (this.selectedPath != this.SelectedItem.GetFullPath())
                    //// this.InitialPath = this.selectedPath;
                    ////}
                    //// else
                    ////{
                    //// this.InitialPath = this.selectedPath;
                    ////}
                    this.NotifyPropertyChanged(() => this.SelectedPath);
                }
                //// this.txtPath.Text = value;  // DBa set path into path text box
            }
        }

        #region drag & drop
        private Point startPoint = new Point();
        private bool IsDragging
        {
            get;
            set;
        }

        private void TreeView_DragOver(object sender, DragEventArgs e)
        {
            DragOperation(false, sender, e);
        }

        private void TreeView_DragEnter(object sender, DragEventArgs e)
        {
            DragOperation(false, sender, e);
        }

        private void TreeView_Drop(object sender, DragEventArgs e)
        {
            DragOperation(true, sender, e);
        }

        private void DragOperation(bool bDrop, object sender, DragEventArgs e)
        {
            try
            {
                if (e.Data.GetData("ProjectItem") != null)
                {
                    ProjectItem source = (e.Data.GetData("ProjectItem") as FrameworkElement).DataContext as ProjectItem;
                    ProjectItem target = (e.OriginalSource as FrameworkElement).DataContext as ProjectItem;

                    if (source.IsAncestorOf(target) || source == target || target.IsFolder == false)
                    {
                        e.Handled = true;
                        e.Effects = DragDropEffects.None;
                    }
                    else
                    {
                        if (bDrop)
                        {
                            target.MoveFrom(source);
                        }
                        e.Handled = true;
                        e.Effects = DragDropEffects.Move;
                    }
                }
                if (e.Data.GetData("HierarchyItem") != null)
                {
                    HierarchyItem source = (e.Data.GetData("HierarchyItem") as FrameworkElement).DataContext as HierarchyItem;
                    ProjectItem target = (e.OriginalSource as FrameworkElement).DataContext as ProjectItem;
                    if (target.IsFolder == false)
                    {
                        e.Handled = true;
                        e.Effects = DragDropEffects.None;
                    }
                    else
                    {
                        if (bDrop)
                        {
                            string fullPath = target.FullPath + "\\" + source.Name + "." + GameObject.GameObjectFileSuffix;
                            IOManager.Instance.SaveGameObject(source.GameObject, fullPath, true);
                            this.RefreshTree();
                        }
                        e.Handled = true;
                        e.Effects = DragDropEffects.Move;
                    }
                }
            }
            catch (System.Exception)
            {
                e.Handled = true;
                e.Effects = DragDropEffects.None;
            }
        }

        private void TreeView_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            if (IsDragging == false)
                startPoint = e.GetPosition(null);
        }

        private void TreeView_PreviewMouseMove(object sender, MouseEventArgs e)
        {
            if (e.LeftButton == MouseButtonState.Pressed && !IsDragging)
            {
                Point position = e.GetPosition(null);
                if (Math.Abs(position.X - startPoint.X) > SystemParameters.MinimumHorizontalDragDistance ||
                    Math.Abs(position.Y - startPoint.Y) > SystemParameters.MinimumVerticalDragDistance)
                {
                    IsDragging = true;
                    DataObject data = new DataObject();
                    data.SetData("ProjectItem", e.OriginalSource);
                    DragDropEffects de = DragDrop.DoDragDrop(this.TreeView, data, DragDropEffects.Move);
                    IsDragging = false;
                }
            }
        }
        #endregion

        public void CreateNewFolder()
        {
            this.CreateNewFolderImpl(this.SelectedItem);
        }

        public void RefreshTree()
        {
            this.Root = null;

            this.Init();
        }

        public void NotifyPropertyChanged<TProperty>(Expression<Func<TProperty>> property)
        {
            var lambda = (LambdaExpression)property;
            MemberExpression memberExpression;
            if (lambda.Body is UnaryExpression)
            {
                var unaryExpression = (UnaryExpression)lambda.Body;
                memberExpression = (MemberExpression)unaryExpression.Operand;
            }
            else
                memberExpression = (MemberExpression)lambda.Body;

            this.OnPropertyChanged(memberExpression.Member.Name);
        }

        private void OnPropertyChanged(string propertyName)
        {
            if (this.PropertyChanged != null)
            {
                this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }

        private void Init()
        {
            string path = IOManager.Instance.AssetsFolder;
            this.root = new ProjectItem(path, null, (uint)2);

            DataContext = this.root;
            this.Root = this.root; // to notify UI
        }

        private void TreeView_Selected(object sender, RoutedEventArgs e)
        {
            var tvi = e.OriginalSource as TreeViewItem;

            if (tvi != null)
                this.SelectedItem = tvi.DataContext as ProjectItem;
        }

        private void TreeView_Expanded(object sender, RoutedEventArgs e)
        {
            var tvi = e.OriginalSource as TreeViewItem;
            var treeItem = tvi.DataContext as ProjectItem;

            if (treeItem != null)
            {
                treeItem.TryLoadChildren(2);
            }
            else
                throw new Exception();
        }

        /// <summary>
        /// Build a stack (queue) of directory entries from root to subDir and return it
        /// </summary>
        /// <param name="subDir"></param>
        /// <returns></returns>
        private Stack<DirectoryInfo> TraverseUpToRoot(DirectoryInfo subDir)
        {
            if (subDir == null)
                return null;

            if (!subDir.Exists)
                return null;

            Stack<DirectoryInfo> queue = new Stack<DirectoryInfo>();
            queue.Push(subDir);
            DirectoryInfo ti = subDir.Parent;

            while (ti != null)
            {
                queue.Push(ti);
                ti = ti.Parent;
            }

            return queue;
        }

        private void CreateNewFolderImpl(ProjectItem parent)
        {
            try
            {
                if (parent == null)
                    return;

                var parentPath = parent.FullPath;
                var newDirName = this.GenerateNewFolderName(parentPath);
                var newPath = Path.Combine(parentPath, newDirName);

                Directory.CreateDirectory(newPath);

                var childs = parent.Children;
                var newChild = new ProjectItem(newPath, parent);
                childs.Add(newChild);
                parent.Children = new TreeItemList(childs.OrderBy(c => c.Name).ToObservableCollection());

                parent.IsExpanded = true;
            }
            catch (Exception ex)
            {
                MessageBox.Show(string.Format("Can't create new folder. Error: {0}", ex.Message));
            }
        }

        private string GenerateNewFolderName(string parentPath)
        {
            string result = NewFolderName;

            if (Directory.Exists(Path.Combine(parentPath, result)))
            {
                for (int i = 1; i < MaxNewFolderSuffix; ++i)
                {
                    var nameWithIndex = string.Format(NewFolderName + " {0}", i);

                    if (!Directory.Exists(Path.Combine(parentPath, nameWithIndex)))
                    {
                        result = nameWithIndex;
                        break;
                    }
                }
            }

            return result;
        }

        private void OpenInExplorer_Click(object sender, RoutedEventArgs e)
        {
            var item = sender as MenuItem;
            if (item != null)
            {
                var context = item.DataContext as ProjectItem;
                ProcessStartInfo psi = new ProcessStartInfo(); 
                psi.WorkingDirectory = "C:\\WINDOWS";      
                psi.FileName = "explorer.exe";
                if (context.IsFolder)
                    psi.Arguments = context.FullPath;
                else
                {
                    string folder = Path.GetDirectoryName(context.FullPath);
                    psi.Arguments = folder;
                }

                Process.Start(psi);
            }
        }

        private void CreateMenuItem_Click(object sender, RoutedEventArgs e)
        {
            var item = sender as MenuItem;
            if (item != null)
            {
                var context = item.DataContext as ProjectItem;
                this.CreateNewFolderImpl(context);
            }
        }

        private void Refresh_Click(object sender, RoutedEventArgs e)
        {
            this.root.IsFullyLoaded = false;
            this.root.TryLoadChildren();
        }

        private void RenameMenuItem_Click(object sender, RoutedEventArgs e)
        {
            // try
            //{
            // var item = sender as MenuItem;
            //    if (item != null)
            // var context = item.DataContext as TreeItem;
            // if (context != null && !(context is DriveTreeItem))
            // var dialog = new InputDialog()
            // Message = "New folder name:",
            // InputText = context.Name,
            // Title = string.Format("Do you really want to rename folder {0}?", context.Name)
            // };
            // if (dialog.ShowDialog() == true)
            // var newFolderName = dialog.InputText;
            // /*
            // * Parent for context is always not null due to the fact
            // * that we don't allow to change the name of DriveTreeItem
            // */
            // var newFolderFullPath = Path.Combine(context.Parent.GetFullPath(), newFolderName);
            // if (Directory.Exists(newFolderFullPath))
            // MessageBox.Show(string.Format("Directory already exists: {0}", newFolderFullPath));
            // else
            // Directory.Move(context.GetFullPath(), newFolderFullPath);
            // context.Name = newFolderName;
            //            }
            // catch (Exception ex)
            // MessageBox.Show(string.Format("Can't rename folder. Error: {0}", ex.Message));
        }

        private void DeleteMenuItem_Click(object sender, RoutedEventArgs e)
        {
            try
            {
                var item = sender as MenuItem;
                if (item != null)
                {
                    var context = item.DataContext as ProjectItem;
                    if (context != null)
                    {
                        var confirmed =
                            MessageBox.Show(
                                string.Format("Do you really want to delete this item {0}?", context.Name),
                                "Confirm folder removal",
                                MessageBoxButton.YesNo);

                        if (confirmed == MessageBoxResult.Yes)
                        {
                            context.TryToDeleteSelf();
                            context.Parent.IsFullyLoaded = false;
                            context.Parent.TryLoadChildren();
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show(string.Format("Can't delete folder. Error: {0}", ex.Message));
            }
        }
    }
}
